package com.faya.green.utils;

import com.faya.green.abnormal.ServiceException;
import com.faya.green.common.Constant;
import com.faya.green.model.CacheProcessor;
import org.apache.shiro.crypto.hash.Md5Hash;

import java.util.Random;

/**
 * 用户 ：LX
 * 创建时间： 2018/7/14. 22:39
 * 地点：广州
 * 目的: 生成token 令牌的工具类，用来防止重复提交
 *          该方法只适合比较适合只能提交一次的表单，需要多次交互的感觉不适合,而且存在同时打开多个页面的也不适合这个，session会被覆盖
 *          对于超时这个建议的方案是将生成的token值当初key保存在session中，并设置超时时间（如果有redis就更好了）,但存在session时间设置后导致其他的也全部被销毁
 *
 *       2018年7月15日21:10:39
 *          update：将token存放在session中的方案放弃，放在缓存中，从而实现类似redis存放token的效果。
 *          具体参考 CacheProcessor 类的实现
 *
 * 结果：
 */
public class TokenUtil {

    /**
     * 单例模式，保证类的对象在内存中只有一个
     */
    private TokenUtil(){}

    private static final TokenUtil token = new TokenUtil();


    /**
     * 判断token是否正确 并将该正确的token失效
     * @param tokenHtml 从页面获取的token
     * @return true 是正确的token false 错误的token
     */
    public boolean isIterateSubmit(String tokenHtml){
        if (CommonUtils.checkNull(tokenHtml)){
            throw new ServiceException("token异常，请重新刷新页面");
        }
        String token = CacheProcessor.getTimedCache().get(tokenHtml);
        if (CommonUtils.checkNull(token)){
            throw new ServiceException("token已经失效，请重新刷新页面");
        }

        if (token.equals(tokenHtml)){
            //移除使用过的 token（也可以覆盖key，同时设置为1毫秒）
            CacheProcessor.getTimedCache().remove(tokenHtml);
            return true;
        }
        return false;
    }

    /**
     * 保存token到session中，需要使用的时候请调用这个方法即可，token有效时间10分钟
     * @return 返回token值，需要保存到页面
     */
    public static String saveTokenSession(){
        String token = getToken();
        //token 的key一定要是总是唯一的， 设置过期时间为 10分钟
        CacheProcessor.getTimedCache().put(token, token, 10 * 60 * 1000);

        //启动定时任务，每过1分钟检查一次过期，如果启动了定时器，那会定时清理缓存中的过期值，但是如果不起动，那只有在get这个值得时候才检查过期并清理，不起动定时器带来的问题是：有些值如果长时间不访问，会占用缓存的空间
        CacheProcessor.getTimedCache().schedulePrune(6000);
        return token;
    }

    /**
     * 返回生成的一个token
     * @return token对象
     */
    private static String getToken(){
        return token.generateTokenStr();
    }

    /**
     * 生成token
     * @return 加密后产生的token
     */
    private String generateTokenStr(){
        //生成token
        String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";

        //使用shiro的md5加密工具加密，参数 1 待加密的字符串  2 盐   3 加密次数
        Md5Hash md5Str = new Md5Hash(token, Constant.TOKEN_SALT, 2);
        return md5Str.toString();
    }

}
